/*
Copyright 2025 PerfXLab (Beijing) Technologies Co., Ltd.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#include "OpenSpB_MACRO.h"
#include "OpenSparseBLAS_func.h"
#include "OpenSparseBLAS_type.h"

/**
 * @ingroup OpenSpB_vector
 * @brief A structure to represent vectors.
 * @details This class stores basic information about a vector, which is inherited by other concrete vector classes of various formats.
 */
struct OpenSpB_vector_opaque
{
    OpenSpB_type element_type;                   /**< Element data type */
    OpenSpB_vector_format_value_ext format_type; /**<  Marker of vector format */
    OpenSpB_index n;                             /**< Vector length.*/
    size_t element_size;                         /**< byte number of a single element.*/
};

/**
 * @ingroup OpenSpB_vector
 * @defgroup OpenSpB_vector_Dense
 * @brief A Dense vector class
 * @details The dense vector format uses a array to represent all elements(including zero elements) in a vector.
 * @{
 */

typedef struct
{
    struct OpenSpB_vector_opaque vector_base;
    void *values;
} OpenSpB_vector_Dense;

OpenSpB_info OpenSpB_vector_Dense_new(OpenSpB_vector *v, OpenSpB_type type, OpenSpB_index n);

OpenSpB_info OpenSpB_vector_Dense_dup(OpenSpB_vector *v1, OpenSpB_vector v2);

#define OpenSpB_VECTOR_DENSE_SET(type, T)                                                     \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(OpenSpB_vector_Dense_setElement_, T) /* w(row) = x */ \
        (                                                                                     \
            OpenSpB_vector w, /* vector to modify           */                                \
            type x,           /* scalar to assign to w(row) */                                \
            OpenSpB_index row /* row index                  */                                \
        )

OpenSpB_VECTOR_DENSE_SET(bool, BOOL);
OpenSpB_VECTOR_DENSE_SET(int8_t, INT8);
OpenSpB_VECTOR_DENSE_SET(int16_t, INT16);
OpenSpB_VECTOR_DENSE_SET(int32_t, INT32);
OpenSpB_VECTOR_DENSE_SET(int64_t, INT64);
OpenSpB_VECTOR_DENSE_SET(uint8_t, UINT8);
OpenSpB_VECTOR_DENSE_SET(uint16_t, UINT16);
OpenSpB_VECTOR_DENSE_SET(uint32_t, UINT32);
OpenSpB_VECTOR_DENSE_SET(uint64_t, UINT64);
OpenSpB_VECTOR_DENSE_SET(float, FP32);
OpenSpB_VECTOR_DENSE_SET(double, FP64);
#undef OpenSpB_VECTOR_DENSE_SET

#define OpenSpB_VECTOR_DENSE_EXTRACT(type, T)                                                   \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(OpenSpB_vector_Dense_extractElement_, T) /* x = v(i) */ \
        (                                                                                       \
            type * x,               /* scalar to extract           */                           \
            const OpenSpB_vector v, /* vector to extract a scalar from */                       \
            OpenSpB_index i         /* index                  */                                \
        )

OpenSpB_VECTOR_DENSE_EXTRACT(bool, BOOL);
OpenSpB_VECTOR_DENSE_EXTRACT(int8_t, INT8);
OpenSpB_VECTOR_DENSE_EXTRACT(int16_t, INT16);
OpenSpB_VECTOR_DENSE_EXTRACT(int32_t, INT32);
OpenSpB_VECTOR_DENSE_EXTRACT(int64_t, INT64);
OpenSpB_VECTOR_DENSE_EXTRACT(uint8_t, UINT8);
OpenSpB_VECTOR_DENSE_EXTRACT(uint16_t, UINT16);
OpenSpB_VECTOR_DENSE_EXTRACT(uint32_t, UINT32);
OpenSpB_VECTOR_DENSE_EXTRACT(uint64_t, UINT64);
OpenSpB_VECTOR_DENSE_EXTRACT(float, FP32);
OpenSpB_VECTOR_DENSE_EXTRACT(double, FP64);
#undef OpenSpB_VECTOR_DENSE_EXTRACT

#define OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(type, T)                                                   \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(OpenSpB_vector_Dense_extractTuples_, T) /*[I,~,X] = find (v)*/ \
        (                                                                                              \
            OpenSpB_index * I,     /*array for returning row indices of tuples*/                       \
            type * X,              /*array for returning values of tuples*/                            \
            OpenSpB_index * nvals, /*I, X size on input; # tuples on output*/                          \
            const OpenSpB_vector v /*vector to extract tuples*/                                        \
        )

OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(bool, BOOL);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(int8_t, INT8);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(int16_t, INT16);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(int32_t, INT32);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(int64_t, INT64);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(uint8_t, UINT8);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(uint16_t, UINT16);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(uint32_t, UINT32);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(uint64_t, UINT64);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(float, FP32);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(double, FP64);

#undef OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES

OpenSpB_info OpenSpB_vector_Dense_free(OpenSpB_vector *v);

/**
 * @anchor OpenSpB_vector_Dense_build
 * @name OpenSpB_vector_Dense_build
 * @brief Store elements from tuples into a dense vetctor
 * @details
 *
 * @param[in,out] w An existing dense Vector object to store the result.
 * @param[in] indices Pointer to an array of indices.
 * @param[in] values Pointer to an array of scalars of a type that is compatible with the domain of vector **w**.
 * @param[in] n The number of entries contained in each array (the same for indices and values).
 * @return OpenSpB_info
 */
///@{

#define OpenSpB_VECTOR_DENSE_BUILD(type, T)                                   \
    OpenSpB_info OpenSpB_vector_Dense_build_##T(OpenSpB_vector_Dense *w,      \
                                                const OpenSpB_index *indices, \
                                                const type *values,           \
                                                OpenSpB_index n);
OpenSpB_VECTOR_DENSE_BUILD(bool, BOOL);
OpenSpB_VECTOR_DENSE_BUILD(int8_t, INT8);
OpenSpB_VECTOR_DENSE_BUILD(int16_t, INT16);
OpenSpB_VECTOR_DENSE_BUILD(int32_t, INT32);
OpenSpB_VECTOR_DENSE_BUILD(int64_t, INT64);
OpenSpB_VECTOR_DENSE_BUILD(uint8_t, UINT8);
OpenSpB_VECTOR_DENSE_BUILD(uint16_t, UINT16);
OpenSpB_VECTOR_DENSE_BUILD(uint32_t, UINT32);
OpenSpB_VECTOR_DENSE_BUILD(uint64_t, UINT64);
OpenSpB_VECTOR_DENSE_BUILD(float, FP32);
OpenSpB_VECTOR_DENSE_BUILD(double, FP64);
#undef OpenSpB_VECTOR_DENSE_BUILD
///@}
/**@}*/ // end for OpenSpB_vector_Dense

/**
 * @ingroup OpenSpB_vector
 * @defgroup OpenSpB_vector_Sparse
 * @brief A sparse vector class
 * @details The sparse vector format uses a list approach to store only all non-zero elements of the vector, wherthe list can be indexed ordered and unordered.
 * @{
 */
typedef struct
{
    struct OpenSpB_vector_opaque vector_base; /**< The base vector msg*/
    void *values;                             /**< The data array */
    OpenSpB_index *indices;                   /**< The index array */
    OpenSpB_index nnz;                        /**< The number of non zero; the size of array valuse and indices*/
    OpenSpB_index capacity;                   /**< The size of the current array request.
                                               * When adding elements to make nnz larger than the capacity, it needs to be expanded*/
    bool is_sorted;                           /**< A notation for whether the array \link values \endlink and \link indices \endlink is ordered. */
} OpenSpB_vector_Sparse;

/**
 * @ingroup OpenSpB_vector_Sparse
 * @brief Create a OpenSpB_vector_Sparse object
 * @param[out] v A Pointer to the newly created sparse vector
 * @param[in] type Type defines the data type of the vector elements
 * @param[in] n Vector length
 * @return OpenSpB_info
 */
OpenSpB_info OpenSpB_vector_Sparse_new(OpenSpB_vector *v, OpenSpB_type type, OpenSpB_index n);

/**
 * @ingroup OpenSpB_vector_Sparse
 * @brief Create OpenSpB_vector_Sparse_setElement_T functions with different data types
 * @param type Macro param: Data Type, e.g., bool, int8_t, int16_t...
 * @param T Macro param: Function Suffix, e.g., BOOL, INT8, INT16...
 * @param[in,out] w The input vector
 * @param[in] x The set value
 * @param[in] row The index of the input vector
 * @return OpenSpB_info
 */
#define OpenSpB_VECTOR_SPARSE_SET(type, T)                                                     \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(OpenSpB_vector_Sparse_setElement_, T) /* w(row) = x */ \
        (                                                                                      \
            OpenSpB_vector w, /* vector to modify           */                                 \
            type x,           /* scalar to assign to w(row) */                                 \
            OpenSpB_index row /* row index                  */                                 \
        )

OpenSpB_VECTOR_SPARSE_SET(bool, BOOL);
OpenSpB_VECTOR_SPARSE_SET(int8_t, INT8);
OpenSpB_VECTOR_SPARSE_SET(int16_t, INT16);
OpenSpB_VECTOR_SPARSE_SET(int32_t, INT32);
OpenSpB_VECTOR_SPARSE_SET(int64_t, INT64);
OpenSpB_VECTOR_SPARSE_SET(uint8_t, UINT8);
OpenSpB_VECTOR_SPARSE_SET(uint16_t, UINT16);
OpenSpB_VECTOR_SPARSE_SET(uint32_t, UINT32);
OpenSpB_VECTOR_SPARSE_SET(uint64_t, UINT64);
OpenSpB_VECTOR_SPARSE_SET(float, FP32);
OpenSpB_VECTOR_SPARSE_SET(double, FP64);
#undef OpenSpB_VECTOR_SPARSE_SET

/**
 * @ingroup OpenSpB_vector_Sparse
 * @brief Create OpenSpB_vector_Sparse_extractElement_T functions with different data types
 * @param type Macro param: Data Type, e.g., bool, int8_t, int16_t...
 * @param T Macro param: Function Suffix, e.g., BOOL, INT8, INT16...
 * @param[out] x The pointer to result
 * @param[in] v The input vector
 * @param[in] row The index of the input vector
 * @return OpenSpB_info
 */
#define OpenSpB_VECTOR_SPARSE_EXTRACT(type, T)                                                   \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(OpenSpB_vector_Sparse_extractElement_, T) /* x = v(i) */ \
        (                                                                                        \
            type * x,               /* scalar to extract           */                            \
            const OpenSpB_vector v, /* vector to extract a scalar from */                        \
            OpenSpB_index i         /* index                  */                                 \
        )

OpenSpB_VECTOR_SPARSE_EXTRACT(bool, BOOL);
OpenSpB_VECTOR_SPARSE_EXTRACT(int8_t, INT8);
OpenSpB_VECTOR_SPARSE_EXTRACT(int16_t, INT16);
OpenSpB_VECTOR_SPARSE_EXTRACT(int32_t, INT32);
OpenSpB_VECTOR_SPARSE_EXTRACT(int64_t, INT64);
OpenSpB_VECTOR_SPARSE_EXTRACT(uint8_t, UINT8);
OpenSpB_VECTOR_SPARSE_EXTRACT(uint16_t, UINT16);
OpenSpB_VECTOR_SPARSE_EXTRACT(uint32_t, UINT32);
OpenSpB_VECTOR_SPARSE_EXTRACT(uint64_t, UINT64);
OpenSpB_VECTOR_SPARSE_EXTRACT(float, FP32);
OpenSpB_VECTOR_SPARSE_EXTRACT(double, FP64);
#undef OpenSpB_VECTOR_SPARSE_EXTRACT

OpenSpB_info OpenSpB_vector_Sparse_extractTuples(OpenSpB_index *indices, int *values, OpenSpB_index *n, const OpenSpB_vector v);
/**
 * @ingroup OpenSpB_vector_Sparse
 * @brief Create OpenSpB_vector_Sparse_extractTuples_T functions with different data types
 * @param type Macro param: Data Type, e.g., bool, int8_t, int16_t...
 * @param T Macro param: Function Suffix, e.g., BOOL, INT8, INT16...
 * @param[in,out] I Array for returning row indices of tuples
 * @param[out] X array for returning values of tuples
 * @param[in] nvals Pointer to \link I \endlink and \link X \endlink size.
 * @param[in] v Vector to extract tuples
 * @return OpenSpB_info
 */
#define OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(type, T)                                                   \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(OpenSpB_vector_Sparse_extractTuples_, T) /*[I,~,X] = find (v)*/ \
        (                                                                                               \
            OpenSpB_index * I,     /*array for returning row indices of tuples*/                        \
            type * X,              /*array for returning values of tuples*/                             \
            OpenSpB_index * nvals, /*I, X size on input; # tuples on output*/                           \
            const OpenSpB_vector v /*vector to extract tuples*/                                         \
        )

OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(bool, BOOL);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(int8_t, INT8);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(int16_t, INT16);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(int32_t, INT32);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(int64_t, INT64);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(uint8_t, UINT8);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(uint16_t, UINT16);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(uint32_t, UINT32);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(uint64_t, UINT64);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(float, FP32);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(double, FP64);

#undef OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES

/**
 * @ingroup OpenSpB_vector_Sparse
 * @brief Frees the space of a sparse vector object
 * @param[in] v A Pointer to the sparse vector needed to be freed
 * @return OpenSpB_info
 */
OpenSpB_info OpenSpB_vector_Sparse_free(OpenSpB_vector *v);

/**
 * @ingroup OpenSpB_vector_Sparse
 * @brief Growing the capacity of a sparse vector
 * @param[in] v A Pointer to the sparse vector needed to be freed
 * @param[in] capacity New capacity to be allocated
 * @return OpenSpB_info
 */
OpenSpB_info OpenSpB_vector_Sparse_grow(OpenSpB_vector_Sparse *v, OpenSpB_index capacity);

/**
 * @anchor OpenSpB_vector_Sparse_build
 * @name OpenSpB_vector_Sparse_build
 * @ingroup OpenSpB_vector_Sparse
 * @brief Store elements from tuples into a vector
 * @details
 * @param[in,out] w An existing Vector object to store the result.
 * @param [in] indices Pointer to an array of indices.
 * @param [in] values Pointer to an array of scalars of a type that is compatible with the domain of vector w.
 * @param [in] n The number of entries contained in each array (the same for indices and values).
 * @return OpenSpB_info
 * @todo Support multiple **dup** operations
 */
///@{
#define OpenSpB_VECTOR_SPARSE_BUILD(type, T)                                   \
    OpenSpB_info OpenSpB_vector_Sparse_build_##T(OpenSpB_vector_Sparse *w,     \
                                                 const OpenSpB_index *indices, \
                                                 const type *values,           \
                                                 OpenSpB_index n)

OpenSpB_VECTOR_SPARSE_BUILD(bool, BOOL);
OpenSpB_VECTOR_SPARSE_BUILD(int8_t, INT8);
OpenSpB_VECTOR_SPARSE_BUILD(int16_t, INT16);
OpenSpB_VECTOR_SPARSE_BUILD(int32_t, INT32);
OpenSpB_VECTOR_SPARSE_BUILD(int64_t, INT64);
OpenSpB_VECTOR_SPARSE_BUILD(uint8_t, UINT8);
OpenSpB_VECTOR_SPARSE_BUILD(uint16_t, UINT16);
OpenSpB_VECTOR_SPARSE_BUILD(uint32_t, UINT32);
OpenSpB_VECTOR_SPARSE_BUILD(uint64_t, UINT64);
OpenSpB_VECTOR_SPARSE_BUILD(float, FP32);
OpenSpB_VECTOR_SPARSE_BUILD(double, FP64);
#undef OpenSpB_VECTOR_SPARSE_BUILD
///@}
/**@}*/ // end for OpenSpB_vector_Sparse